// xp_gethash.cpp
// --------------
// Extended stored procedure DLL for returning the calling SQL users SQL hash.

#include "xp_gethash.h"

BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

// Versioning export
__declspec(dllexport) ULONG __GetXpVersion()
{
   return ODS_VERSION;
}

// Added for xp_loadlibrary() usage
__declspec(dllexport) bool xp_gethashlib(SRV_PROC *srvproc, TCHAR* tcParameters)
{
	// Call the original xsproc
	if(XP_NOERROR == xp_gethash(srvproc))
		return(true);
	else
		return(false);
}

// Xsproc entry point
__declspec(dllexport) SRVRETCODE xp_gethash(SRV_PROC *srvproc)
{
	SRVRETCODE	vRetCode;

	// Call the function and return
	vRetCode = SqlGetHash(srvproc);

	return(vRetCode);
}

// Send a SQL msg (non-error)
SRVRETCODE Sql_SendMsg(SRV_PROC *srvproc,TCHAR* tczMsg)
{
	int	iResult = 0;
	// Both the ASCII and UNICODE calls are assuming a NULL terminated string
#ifdef UNICODE
	// wcslen() is used here because 'SRV_NULLTERM' seems to always result in FAILURE with srv_wsendmsg()
	iResult = srv_wsendmsg(srvproc,(DBTINYINT)0,(DBTINYINT)0,tczMsg,wcslen(tczMsg));//SRV_NULLTERM);
#else
	iResult = srv_sendmsg(srvproc, SRV_MSG_INFO, 0,(DBTINYINT)0,(DBTINYINT)0,NULL,0,0,tczMsg,strlen((CHAR*)tczMsg));
#endif
	if(iResult != SUCCEED)
		return(Sql_SendErr(srvproc,_T("Sql_SendMsg failure.")));
	return(SUCCEED);
}

// Send a SQL msg (error)
SRVRETCODE Sql_SendErr(SRV_PROC *srvproc,TCHAR* tczErr)
{
	int	iResult = 0;
	// Both the ASCII and UNICODE calls are assuming a NULL terminated string
#ifdef UNICODE
	// wcslen() is used here because 'SRV_NULLTERM' seems to always result in FAILURE with srv_wsendmsg()
	iResult = srv_wsendmsg(srvproc,0,SRV_MSG_ERROR,tczErr,wcslen(tczErr));//SRV_NULLTERM);
#else
	iResult = srv_sendmsg(srvproc, SRV_MSG_ERROR, 0,(DBTINYINT)0,(DBTINYINT)0,NULL,0,0,tczErr,strlen((CHAR*)tczErr));
#endif
	if(SUCCEED != iResult)
		return(FAIL);
	return(SUCCEED);
}

// Prepare the columns for returned data
SRVRETCODE Sql_PrepRows(SRV_PROC *srvproc)
{

#ifdef UNICODE
	if(!srv_describe(srvproc,1,NULL,0,SRVNVARCHAR,RESULTS_MAX_COL_SIZE,SRVNVARCHAR,0,NULL))
		return(FAIL);
#else
	if(!srv_describe(srvproc,1,NULL,0,SRVBIGVARCHAR,RESULTS_MAX_COL_SIZE,SRVBIGVARCHAR,0,NULL))
		return(FAIL);
#endif

	return(SUCCEED);
}

// Send a row of data back to the client
SRVRETCODE Sql_SendRow(SRV_PROC *srvproc,TCHAR* tczMsg)
{
	// Set the column data length
	if(FAIL == srv_setcollen(srvproc,1,(_tcslen(tczMsg) * sizeof(TCHAR))))
		return(FAIL);

	// Set the column data
	if(FAIL == srv_setcoldata(srvproc,1,tczMsg))
		return(FAIL);

	// Send the data
	if(FAIL == srv_sendrow(srvproc))
		return(FAIL);
	
	// Increment the row counter
	dwTOTALROWS++;

	return(SUCCEED);
}

// Close out the result set(s)
SRVRETCODE Sql_RowsDone(SRV_PROC *srvproc)
{
	// Determine if row count should be provided
	if(0 <= dwTOTALROWS)
		srv_senddone(srvproc, SRV_DONE_COUNT, 0, dwTOTALROWS);
	else
		srv_senddone(srvproc, SRV_DONE_ERROR, 0, 0);

	return(SUCCEED);
}

// Queries for the current sessions SQL hash
SRVRETCODE SqlGetHash(SRV_PROC *srvproc)
{
	TCHAR*		tczReturnString = NULL;
	TCHAR*		tczUsername = NULL;
	BYTE*		bpPasswordHash = NULL;
	DWORD		dwCharsConverted = 0;
	int			iInfoLen = 0;
	int			iHashLen = 0;
	int			iStringLen = 0;
	TCHAR		tcHex[3];

	srv_pfield(srvproc,SRV_LSECURE,&iInfoLen);
	if(0 < iInfoLen)
	{
		if(6 == iInfoLen)
		{
			// Get the SQL password hash
			iInfoLen = 0;
			srv_pfieldex(srvproc,SRV_HASHPWD,&iInfoLen);
			if(0 < iInfoLen)
			{
				// Allocate space for the hashed password bytes
				iHashLen = iInfoLen;
				bpPasswordHash = new BYTE[iHashLen];
				memset(bpPasswordHash,0,iHashLen);
				memcpy(bpPasswordHash,srv_pfieldex(srvproc,SRV_HASHPWD,&iInfoLen),iHashLen);
			}
			else
			{
				// Unable to get the password hash
				Sql_SendErr(srvproc,_T("Unable to retrieve SQL hash."));
				return(XP_ERROR);
			}

			// Build the return string
			iStringLen = (iHashLen * 2) + 3; // NULL + 0x characters
			tczReturnString = new TCHAR[iStringLen];
			memset(tczReturnString,0,iStringLen * sizeof(TCHAR));
			_tcscat_s(tczReturnString,iStringLen,_T("0x"));
			for(int x = 0; x < iHashLen; x++)
			{
				_stprintf_s(tcHex,3,_T("%02x"),bpPasswordHash[x]);
				_tcscat_s(tczReturnString,iStringLen,tcHex);
			}

			// Send the string back to the user
			if(FAIL == Sql_PrepRows(srvproc))
			{
				// Failed to initialize returns
				Sql_SendErr(srvproc,_T("Sql_PrepRows() error"));
				return(XP_ERROR);
			}
			if(FAIL == Sql_SendRow(srvproc,tczReturnString))
			{
				Sql_SendErr(srvproc,_T("Sql_SendRow() error"));
				return(XP_ERROR);
			}
			Sql_RowsDone(srvproc);
		}
		else
		{
			// Not using SQL auth
			Sql_SendErr(srvproc,_T("Current session not using SQL authentication."));
		}
	}
	else
	{
		// Failed to determine authentication type
		Sql_SendErr(srvproc,_T("Unable to determine authentication."));
		return(XP_ERROR);
	}
	return(XP_NOERROR);
}